/*
 * Copyright (C) 2009 Leandro de Oliveira Aparecido <lehphyro@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.japa.util;

import java.lang.reflect.*;

import javax.persistence.*;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.*;

/**
 * Utilities to deal with JPA.
 * 
 * @author Leandro Aparecido
 * @since 1.0
 */
//TODO Refactor a little and implement support for composite primary key
public abstract class JpaUtils {

	public static final String LIKE_ANY_TOKEN = "%";
	public static final String ESCAPED_LIKE_ANY_TOKEN = "\\%";
	public static final String LIKE_SINGLE_TOKEN = "_";
	public static final String ESCAPED_LIKE_SINGLE_TOKEN = "\\_";

	public static String forLike(String value) {
		if (StringUtils.isBlank(value)) {
			return null;
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append(LIKE_ANY_TOKEN);
		sb.append(escape(value));
		sb.append(LIKE_ANY_TOKEN);
		
		return sb.toString();
	}
	
	public static String forStartsWith(String value) {
		if (StringUtils.isBlank(value)) {
			return null;
		}
		
		StringBuilder sb = new StringBuilder();
		sb.append(LIKE_ANY_TOKEN);
		sb.append(escape(value));
		
		return sb.toString();
	}

	public static String escape(String valueToEscape) {
		valueToEscape = StringUtils.replaceChars(valueToEscape, LIKE_ANY_TOKEN, ESCAPED_LIKE_ANY_TOKEN);
		return StringUtils.replaceChars(valueToEscape, LIKE_SINGLE_TOKEN, ESCAPED_LIKE_SINGLE_TOKEN);
	}

	/**
	 * Retrieves the primary key column names of an entity.
	 * 
	 * @param entityClass Class of the entity
	 * @return Names of the primary keys.
	 */
	public static String[] getPrimaryKeyNames(Class<?> entityClass) {
		final String[] names = new String[1];
		ReflectionUtils.doWithFields(entityClass, new ReflectionUtils.FieldCallback() {
			public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
				if (field.isAnnotationPresent(Column.class)) {
					Column column = field.getAnnotation(Column.class);
					if ("".equals(column.name())) {
						names[0] = field.getName();
					} else {
						names[0] = column.name();
					}
				} else {
					names[0] = field.getName();
				}
			}
		}, new ReflectionUtils.FieldFilter() {
			public boolean matches(Field field) {
				return field.isAnnotationPresent(Id.class);
			}
		});
		
		if (names[0] == null) {
			ReflectionUtils.doWithMethods(entityClass, new ReflectionUtils.MethodCallback() {
				public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
					if (method.isAnnotationPresent(Column.class)) {
						Column column = method.getAnnotation(Column.class);
						if ("".equals(column.name())) {
							names[0] = StringUtils.uncapitalize(method.getName().substring(3));
						} else {
							names[0] = column.name();
						}
					} else {
						names[0] = StringUtils.uncapitalize(method.getName().substring(3));
					}
				}
			}, new ReflectionUtils.MethodFilter() {
				public boolean matches(Method method) {
					return method.isAnnotationPresent(Id.class);
				}
			});
		}
		
		return names;
	}
	
}
